/***
* ==++==
*
* Copyright (c) Microsoft Corporation.  All rights reserved.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* http_utils.h
*
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/

#pragma once

#include "xxpublic.h"
#include <string>
#include <vector>


namespace utilities
{

/// <summary>
/// Represents a Windows Error Code as an exception
/// </summary>
class win32_exception : public std::exception
{
public:
    explicit win32_exception(unsigned long error)
        : m_error(error), m_message(get_error_message(error))
    { }

    virtual const char *what() const
    {
        return m_message.c_str();
    }

    unsigned long error_code()
    {
        return m_error;
    }
    static _ASYNCRTIMP std::string __cdecl get_error_message(unsigned long error);

private:
    unsigned long m_error;
    std::string m_message;
};

class conversions
{
public:
    /// <summary>
    /// Converts a wstring to a (UTF-8) string using windows apis
    /// </summary>
    static _ASYNCRTIMP std::string __cdecl utf16_to_utf8(const std::wstring &w);

    /// <summary>
    /// Converts a (UTF-8) string to a wstring using windows apis
    /// </summary>
    static _ASYNCRTIMP std::wstring __cdecl utf8_to_utf16(const std::string &s);

    /// <summary>
    /// Converts a Latin1 (iso-8859-1) string to a UTF-8 string using windows apis.
    /// </summary>
    static _ASYNCRTIMP std::string __cdecl latin1_to_utf8(const std::string &s);

    /// <summary>
    /// Converts a ASCII (us-ascii) string to a UTF-16 string using windows apis.
    /// </summary>
    static _ASYNCRTIMP std::wstring __cdecl usascii_to_utf16(const std::string &s);

    /// <summary>
    /// Converts a Latin1 (iso-8859-1) string to a UTF-16 string using windows apis.
    /// </summary>
    static _ASYNCRTIMP std::wstring __cdecl latin1_to_utf16(const std::string &s);

    /// <summary>
    /// Converts a string with the OS's default code page to a UTF-8 string using windows apis.
    /// </summary>
    static _ASYNCRTIMP std::string __cdecl default_code_page_to_utf8(const std::string &s);

    /// <summary>
    /// Encode the given byte array into a bas64 string
    /// </summary>
    static _ASYNCRTIMP std::string __cdecl to_base64(const std::vector<unsigned char>& data);

    /// <summary>
    /// Decode the given base64 string to a byte array
    /// </summary>
    static _ASYNCRTIMP std::vector<unsigned char> __cdecl from_base64(const std::string& str);

};

class datetime
{
public:
    typedef unsigned long long interval_type;

    /// <summary>
    /// Returns the current UTC time. 
    /// </summary>
    static _ASYNCRTIMP datetime __cdecl utc_now();

    /// <summary>
    /// Creates datetime from a string representing time in UTC in RFC 1123 format.
    /// </summary>
    static _ASYNCRTIMP datetime __cdecl from_string(const std::wstring& timestring);

    datetime() : m_interval(0)
    {
    }

    /// <summary>
    /// Returns a string representation of the datetime. The string is formatted based on RFC 1123
    /// </summary>
    _ASYNCRTIMP std::wstring to_wstring();

    /// <summary>
    /// Returns a string representation of the datetime. The string is formatted based on RFC 1123
    /// </summary>
    std::string to_string()
    {
        return utilities::conversions::utf16_to_utf8(to_wstring());
    }

    datetime operator- (int value) const
    {
        return datetime(m_interval - value);
    }

    datetime operator+ (int value) const
    {
        return datetime(m_interval + value);
    }

    bool operator== (datetime dt) const
    {
        return m_interval == dt.m_interval;
    }

    bool operator!= (const datetime& dt) const
    {
        return !(*this == dt);
    }

private:

    friend int operator- (datetime t1, datetime t2);

    // Private constructor. Use static methods to create an instance.
    datetime(interval_type interval) : m_interval(interval)
    {
    }

    interval_type m_interval;
};

inline int operator- (datetime t1, datetime t2)
{
    auto diff = (t1.m_interval - t2.m_interval);

    // Round it down to seconds
    diff /= 10 * 1000 * 1000;
    
    return (int) (diff);
}

} // namespace utilities;